
#include "mem.h"
#include "macros.h"
#include "list.h"

struct _node {
    void * data;
    struct _node * next;
    struct _node * prev;
};

struct _list {
    long int size;
    struct _node * start;
    struct _node * curr;
};

typedef struct _list ListPriv;

static struct _node * node_new(void * data);

List * list_new(void) {

    ListPriv * lp;

    /* alloc */
    lp = calloc(1, sizeof (ListPriv));

    _verify(lp);

    /* constructor */
    lp->size = 0;
    lp->start = 0x0;
    lp->curr = 0x0;

    return (List *) lp;
}

long int list_get_size(List * list) {
    ListPriv * lp;
    lp = (ListPriv *) list;
    return lp->size;
}

void * list_insert(List * list, void * data) {

    ListPriv * lp;
    struct _node * ln;

    _verify(list);

    lp = (ListPriv *) list;

    ln = lp->start;

    if (ln == NULL) {

        ln = node_new(data);

        ln->next = ln;
        ln->prev = ln;

        lp->size = 1;
        lp->start = ln;
        lp->curr = ln;

        return data;
    }

#ifdef AVOID_SAME_INSERTION

    /* Avoid same insertion */
    ln = lp->start;
    do {
        if (ln->data == data) return data;
        ln = ln->next;
    } while (ln != lp->start);

#endif

    ln = node_new(data);
    ln->next = lp->start;
    ln->prev = lp->start->prev;

    lp->start->next = ln;
    lp->start->prev = ln;
    lp->size++;

    return data;
}

void * list_remove(List * list, void * data) {

    ListPriv * lp;
    struct _node * ln;

    _verify(list);
    lp = (ListPriv *) list;

    /* Empty list */
    if (lp->start == NULL) return NULL;

    /* Find data */
    ln = lp->start;
    do {
        if (ln->data == data) break;
        ln = ln->next;
    } while (ln != lp->start);

    /* Not found */
    if (ln->data != data) {
        return NULL;
    }

    /* Found! */
    if (lp->size == 1) {

        lp->size = 0;
        lp->start = NULL;

        if (lp->curr == ln) lp->curr = NULL;

    } else {

        ln->next->prev = ln->prev;
        ln->prev->next = ln->next;
        lp->size--;
        if (ln == lp->start) lp->start = ln->next;

        if (lp->curr == ln) lp->curr = ln->next;
    }

    mp_free(ln);

    return data;
}

void * list_get_next(List * list) {

    ListPriv * lp;
    void * data;

    lp = (ListPriv *) list;
    if (lp->curr) {
        data = lp->curr->data;
        lp->curr = lp->curr->next;
        return data;
    } else {
        return NULL;
    }
}

void * list_get_data(List * list, long int idx) {

    ListPriv * lp;
    struct _node * ln;
    void * data;
    int n;

    _verify(list);

    lp = (ListPriv *) list;

    if (idx >= lp->size) {
        return NULL;
    }

    ln = lp->start;
    while( idx ) {
        ln = ln->next;
        idx--;
    }
    
    return ln->data;
}

void list_reset(List * list) {
    ListPriv * lp;
    lp = (ListPriv *) list;
    lp->curr = lp->start;
}

void list_clear(List * list, void (*data_free)(void *)) {
    ListPriv * lp;
    struct _node * node;
    struct _node * ant;

    lp = (ListPriv *) list;

    node = lp->start;
    while (node) {
        data_free(node->data);
        ant = node;
        node = node->next;
        mp_free(ant);
    }

    lp->size = 0;
    lp->start = 0x0;
    lp->curr = 0x0;
}

void list_delete(List * list) {

    _verify(list);

    list_clear(list, free);

    mp_free(list);
}


// static methods

static struct _node * node_new(void * data) {
    struct _node * node;
    node = mp_alloc(struct _node);
    node->next = 0x0;
    node->prev = 0x0;
    node->data = data;
    return node;
}


